package com.bittech.httpd.core;

import com.bittech.httpd.common.HttpStatus;
import com.bittech.httpd.config.ServerConfig;
import com.bittech.httpd.handler.AboutHandler;
import com.bittech.httpd.handler.IndexHandler;
import com.bittech.httpd.handler.NotFoundHandler;
import com.bittech.httpd.handler.NotSupportedMethodHandler;
import com.bittech.httpd.handler.ServerErrorHandler;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Author: secondriver
 * Created: 2018/8/21
 */
public abstract class HttpServer {
    
    static final String SERVER_CONFIG_KEY = "serverConfig";
    
    static final String STATIC_URL = "_default_";
    
    private static final String ROOT_URL = "/";
    
    private static final String INDEX_URL = "/index";
    
    private static final String ABOUT_URL = "/about";
    
    private static final Map<String, Object> context = new ConcurrentHashMap<>();
    
    /**
     * 服务配置
     */
    private final ServerConfig serverConfig;
    
    private final AtomicBoolean serverStatus = new AtomicBoolean(true);
    
    /**
     * 线程调度池
     */
    private final ExecutorService executorService = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2,
            new ThreadFactory() {
                
                private final AtomicInteger index = new AtomicInteger(0);
                
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    thread.setName("Http-Handler-Thread-" + index.getAndIncrement());
                    return thread;
                }
            });
    
    
    public HttpServer(ServerConfig serverConfig) {
        this.serverConfig = serverConfig;
    }
    
    public void start() {
        try {
            //1.加载上下文信息
            this.loadContext();
            //2.创建服务端Socket
            ServerSocket serverSocket = new ServerSocket(serverConfig.getPort());
            //3.创建分发处理器
            DispatcherHandler dispatcherHandler = new DispatcherHandler(context);
            //4.加载处理器
            this.loadHandler(dispatcherHandler);
            while (serverStatus.get()) {
                //接收客户端连接
                final Socket socket = serverSocket.accept();
                executorService.execute(dispatcherHandler.handler(socket));
            }
        } catch (IOException e) {
            System.out.println("Server started occur error " + e.getMessage());
        }
    }
    
    public void stop() {
        this.executorService.shutdown();
        this.serverStatus.set(false);
    }
    
    /**
     * 加载处理器
     */
    private void loadHandler(final DispatcherHandler dispatcherHandler) {
        this.builtInHandler(dispatcherHandler);
        this.registerHandler(dispatcherHandler);
    }
    
    /**
     * 加载上下文变量
     */
    private void loadContext() {
        context.put(SERVER_CONFIG_KEY, serverConfig);
        this.registerContext(context);
    }
    
    /**
     * 内置处理器
     */
    private void builtInHandler(final DispatcherHandler dispatcherHandler) {
        //默认加载静态资源处理器
        dispatcherHandler.register(STATIC_URL, BuiltInResourceHandler.class);
        dispatcherHandler.register(HttpStatus.NOT_FOUND, NotFoundHandler.class);
        dispatcherHandler.register(HttpStatus.METHOD_NOT_ALLOWED, NotSupportedMethodHandler.class);
        dispatcherHandler.register(HttpStatus.INTERNAL_ERROR, ServerErrorHandler.class);
        //内置欢迎页面
        dispatcherHandler.register(new String[]{INDEX_URL, ROOT_URL}, IndexHandler.class);
        dispatcherHandler.register(ABOUT_URL, new AboutHandler());
    }
    
    /**
     * 注册处理器
     */
    public abstract void registerHandler(final DispatcherHandler dispatcherHandler);
    
    /**
     * 注册上下文变量
     */
    public abstract void registerContext(final Map<String, Object> context);
}
